-
Notifications
You must be signed in to change notification settings - Fork 415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support DensityInterface API #1416
Conversation
c637675
to
b8ed8f9
Compare
Codecov Report
@@ Coverage Diff @@
## master #1416 +/- ##
==========================================
+ Coverage 83.60% 83.64% +0.04%
==========================================
Files 118 119 +1
Lines 6891 6899 +8
==========================================
+ Hits 5761 5771 +10
+ Misses 1130 1128 -2
Continue to review full report at Codecov.
|
Failures on nightly seem unrelated. |
Can you write more about what this would be good for, maybe a specific use case, also compared to the alternative of just creating an anonymous function |
Sure. I think @phipsgabler put it very nicely in TuringLang/AbstractPPL.jl#38: "
All of this works much better, of course, if
Using plain log-density functions can be very awkward at higher levels of code. To illustrate, in Julia examples for Bayesian analysis I used to write On the other hand, But There's also the fact that one doesn't always want the log value of a density: When integrating, for example, the algorithm typically needs the non-log values of the density. In many cases, it will be fine to use DensityInterface is not limited to Bayesian applications of course - in fact, it's intended to be very general. The current API originated through (I think very productive) discussions (JuliaMath/DensityInterface.jl#1, JuliaMath/DensityInterface.jl#3, JuliaMath/DensityInterface.jl#4) among several people who have a an interest in (log-)densities in the Julia community and are involved in packages that would profit from this. Maintainers of Distributions were also involved. Having Distributions support DensityInterface was an important aspect of the design. DensityInterface is an extremely lightweight package with no dependencies beyond |
Added I think this is complete now. |
Co-authored-by: David Widmann <devmotion@users.noreply.github.com>
@devmotion, now that we have |
I'd say specialize to Something related: I remember one bug originating from a distribution which did it the other way round and implemented |
Seems more natural to me as well (it's what this PR currently does). |
It should call
I think I fixed all of these potential StackOverflowErrors, the dispatching should be much more sane nowadays. |
Ok, I took the specialization of |
@mschauer ,@devmotion, is this fine like this from your side? |
1f61527
to
10ed991
Compare
Also make logfuncdensity call logpdf for distributions.
CI will fail, requires JuliaMath/DensityInterface.jl#6 |
Have to wait for JuliaRegistries/General#48369 |
Oh, it did pass on Julia v1.0 and v1.6, actually. It failed for me locally on v1.7 without JuliaMath/DensityInterface.jl#6. But you can review the changes already. :-) |
Test failures on nightly seem unrelated. Ready for a fresh review round. :-) |
I find |
I has similar concerns, but from what I understood Turing actually depends on having densities that will work for both single and multiple samples - correct, @devmotion ? |
Yes, in expressions of the form Also implementation-wise there are no ambiguities as it is clear from the type of the distribution if |
I'm also ambivalent about it. But one could argue that things like |
Maybe we could do the following, as I'm also not particularly satisfied with the slightly unusual design of
|
So we just move IIDDensity to DynamicPPL? Sounds good. |
Ok, here's our current IIDDensity implementation, to make it easy to find for DynamicPPL later on: """
IIDDensity(d::Distribution)
Represents the probability density of an implicit product distribution of
variates that are identically and independently distributed according to
the distribution `d`.
Use `DensityInterface.logdensityof(d, x)` to compute the logarithmic density
value at `x`. `x` may be a single variate of `d` or a whole set of variates
of `d`.
If `x` is a single variate of `d`, the density is the PDF of `d`.
If `x` is a set of variates (given as a higher-dimensional array or
and array of arrays), the density is the PDF of an implicit product
distribution over `d`, the size of the product is implied by the size of
the set.
`DensityInterface.logdensityof(IIDDensity(d::Distribution), x)` is equivalent
to `loglikelihood(d, x)`.
"""
struct IIDDensity{D<:Distribution}
distribution::D
end
@inline DensityInterface.hasdensity(d::IIDDensity) = true
# ToDo: Move documentation of behavior of `logdensityof(d::IIDDensity, x)` to
# a method docstring of logdensityof here if/when IIDDensity becomes exported.
DensityInterface.logdensityof(d::IIDDensity, x) = loglikelihood(d.distribution, x)
# =================================================
@testset "IIDDensity" begin
using DensityInterface
d_mv = MvNormal([2.3 0.4; 0.4 1.2])
x = [rand(d_mv) for i in 1:3]
ref_logd_at_x = loglikelihood(d_mv, x)
d = Distributions.IIDDensity(d_mv)
DensityInterface.test_density_interface(d, x, ref_logd_at_x)
DensityInterface.test_density_interface(d, hcat(x...), ref_logd_at_x)
# Stricter than required by test_density_interface:
@test logfuncdensity(logdensityof(d)) === d
end We also had this text in the docs You can use DensityInterface.logdensityof(Distributions.IIDDensity(d), x) and DensityInterface.densityof(Distributions.IIDDensity(d), x) to evaluate the log-density and density of variate value(s) |
Ok, |
@devmotion and @mschauer, Ok with you in the new state? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, can you update the version number?
Done. Let's wait until we've resolved the |
Ok, I think we're through, all good from my side. |
I merged this, trusting that we revisit how to allow the DensityInterface to preserve and make available within the interface some of the information and context which defines what kind of object |
Absolutely. From my side, DensityInterface is a beginning, not the end of this story. |
Implements
DensityInterface.hasdensity(::Distribution)
andDensityInterface.logdensityof(d::Distribution, x)
.This will form a basis for using distributions in generic code that needs to handle distributions as well as (non-normalized) densities.
CC @cscherrer, @devmotion, @mschauer, @phipsgabler, @tpapp.